home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Utilities / Programming / EnterAct 3.7.3 / Drag_on Modules / hAWK programs / $FormatFunctionIntro < prev    next >
Encoding:
Text File  |  1996-05-21  |  21.3 KB  |  876 lines  |  [TEXT/KEEN]

  1. # $FormatFunctionIntro: adapted from $ClipFormatFuncIntro.
  2. # Pretty up the start of a function, down to last local
  3. # variable at beginning of function definition.
  4. # Use the "Set Variables" button in the hAWK dialog to set the value
  5. # of spaces_in_tab if it is different from 4.
  6. # The preset variable "convert_comments", if left to its default value of 1,
  7. # will signal conversion of /*...*/ comments to one or more // comments.
  8. # Set it to 0 if you don't want this.
  9.  
  10. # Usage: this is a one-shot version of $ClipFormatFuncIntro, adapted for
  11. # calling with a command line. The code template "ff" in the file
  12. # "EnterAct Code Templates" expands into a command line for this program.
  13. # We reformat a single function start and then quit, returning the reformatted
  14. # start in stdout. NOTE you don't need to type "ff" at the start of the function.
  15. # -select the entire function start, from start of return type down to
  16. #     the end of local variable declarations at the function's top
  17. # Copy
  18. # -somewhere else, type "ff" and press <command><return> twice
  19. # -the reformatted function start will appear in stdout
  20.  
  21. # Here's an example:
  22.  
  23. # Boolean AddSimilarFiles(Boolean isSysHeader, long *fileKeyPtr) /* start of messy function */
  24. #    {
  25. #    WindowDataPtr    wdPtr = GetProjectPtr(); // hey it's a standard window data pointer
  26. #    char            fileName[32];    /*comment broken across
  27. #                                    two lines */
  28. #    long            startDirID;/* directory to start search from */
  29. #    short                j, index, vRefNum, whichPane;
  30. #    short            q=2;//completely arbitrary comment here
  31. #    Boolean            filesAdded = FALSE;
  32. #    Boolean            searchSubs = (gEvtDetails.optionDown || gEvtDetails.shiftDown);
  33.  
  34. # ...select that, Copy, type ff <cmd><return><cmd><return>, and stdout appears:
  35.  
  36. #Boolean AddSimilarFiles
  37. #    (Boolean    isSysHeader,    // 
  38. #    long        *fileKeyPtr)    // start of messy function 
  39. #    {
  40. #    WindowDataPtr        wdPtr = GetProjectPtr();// hey it's a standard window data pointer
  41. #    char                fileName[ 32 ];        // comment broken across
  42. #                                    // two lines 
  43. #    long                startDirID;            // directory to start search from 
  44. #    short                j;                    // 
  45. #    short                index;                // 
  46. #    short                vRefNum;            // 
  47. #    short                whichPane;            // 
  48. #    short                q =2;                // completely arbitrary comment here
  49. #    Boolean                filesAdded = FALSE;    // 
  50. #    Boolean                searchSubs = (gEvtDetails.optionDown || gEvtDetails.shiftDown);// 
  51.  
  52. # This still has some minor problems -- eg the "start of messy function" comment will probably
  53. # have to be moved up to just above the start of the function - but it was a pretty lame
  54. # comment anyway, and shouldn't really have been there. The multi-line comment that covers
  55. # two lines has been converted to two single-line comments, but the tabbing will have to
  56. # be adjusted. The huge last line might look better if split across two lines.
  57.  
  58.  
  59. BEGIN {
  60.     init();
  61.     # Check user-set variables
  62.     if (spaces_in_tab+0 < 1)
  63.         spaces_in_tab = 4;
  64.     if (convert_comments+0 != 0)
  65.         convert_comments = 1;
  66.     ReformatFuncIntro();
  67.     }
  68.  
  69. function ReformatFuncIntro()
  70.     {
  71.     # clear toktype, error flag, output
  72.     toktype = 0;
  73.     error = 0;
  74.     out = ""
  75.     var_number = 0;
  76.     last_param = 0;
  77.     last_local = 0;
  78.     # also clean out arrays
  79.     clean_arrays();
  80.     clip = getclip(); # gets calling editor's private clip
  81.     # parse the clip as a function beginning
  82.     func_beginning();
  83.     # output the reformatted function beginning to "out"
  84.     if (error == 0)
  85.         output_reformatted_beginning();
  86.     # send the result back to stdout
  87.     if (error == 0)
  88.         OutputResult();
  89.     }
  90.  
  91. # Break "out" into lines and print to stdout.
  92. function OutputResult()
  93.     {
  94.     numLines = split(out, outArray, RS);
  95.     for (i = 1; i <= numLines; ++i)
  96.         print outArray[i];
  97.     }
  98.  
  99. # clean out var_type[i] var_name[i] var_init[i] var_comment[i]
  100. function clean_arrays()
  101.     {
  102.     for (i in var_type)
  103.         delete var_type[i];
  104.     for (i in var_name)
  105.         delete var_name[i];
  106.     for (i in var_init)
  107.         delete var_init[i];
  108.     for (i in var_comment)
  109.         delete var_comment[i];    
  110.     }
  111.  
  112. # parse the clip as a function beginning
  113. # func_beginning    :    stuff_before_paren '(' param_decls ')' '{' local_decls
  114. function func_beginning()
  115.     {
  116.     # collect stuff_before_paren
  117.     stuff_before_paren();
  118.     # require hit '(' here
  119.     advance();
  120.     require(tok == "(", "No opening paren");
  121.     if (error) return;
  122.     advance();
  123.     # collect param declarations
  124.     param_decls();
  125.     # require ')'
  126.     require(tok == ")", "No closing paren");
  127.     if (error) return;
  128.     advance();
  129.     # pick up any comment after ')'
  130.     if (toktype == COMMENT)
  131.         {
  132.         var_comment[var_number] = tok;
  133.         advance();
  134.         }
  135.     if (error) return;
  136.     # record divider between params and locals
  137.     last_param = var_number;
  138.     # if '{' follows, do the locals too
  139.     if (tok == "{")
  140.         {
  141.         advance();
  142.         # collect local declarations
  143.         local_decls();
  144.         last_local = var_number;
  145.         }
  146.     else if (toktype != EOF)
  147.         require(0, "expected { or nothing after parameters");
  148.     }
  149.  
  150. # match up to just before '(', wherever it is
  151. function stuff_before_paren()
  152.     {
  153.     if (match(clip, /\(/))
  154.         {
  155.         opening = substr(clip, 1, RSTART-1)
  156.         clip = substr(clip, RSTART)
  157.         }
  158.     }
  159.  
  160. # param_decls        :    param_decl {{comment}','{comment} paramdecl}
  161. function param_decls()
  162.     {
  163.     
  164.     while (tok != ")" && toktype != EOF)
  165.         {
  166.         param_decl();
  167.         if (toktype == COMMENT)
  168.             {
  169.             # record the comment
  170.             var_comment[var_number] = tok;
  171.             advance();
  172.             if (tok == ",")
  173.                 {
  174.                 advance();
  175.                 if (toktype == COMMENT)
  176.                     {
  177.                     # record the comment
  178.                     var_comment[var_number] = tok;
  179.                     advance();
  180.                     }
  181.                 }
  182.             }
  183.         else if (tok != "," && tok != ")" && toktype != EOF)
  184.             {
  185.             require(0, "illegal looking params");
  186.             }
  187.         if (tok == ",")
  188.             {
  189.             advance();
  190.             if (toktype == COMMENT)
  191.                 {
  192.                 # record the comment
  193.                 var_comment[var_number] = tok;
  194.                 advance();
  195.                 }
  196.             }
  197.         }
  198.     require(toktype != EOF, "unexpected EOF in function parameters");
  199.     }
  200.  
  201. # param_decl    :    type_name [star_name]
  202. # type_name        :    name | '...'
  203. function param_decl()
  204.     {
  205.     if (toktype == NAME)
  206.         {
  207.         # collect various keywords that might come before a type name proper
  208.         if (tok == "static" || tok == "const" || tok == "volatile"\
  209.         || tok == "extern" || tok == "register"\
  210.         || tok == "signed" || tok == "unsigned")
  211.             {
  212.             var_type[++var_number] = tok;
  213.             advance();
  214.             while (tok == "static" || tok == "const" || tok == "volatile"\
  215.                 || tok == "extern" || tok == "register"\
  216.                 || tok == "signed" || tok == "unsigned")
  217.                 {
  218.                 var_type[var_number] = var_type[var_number] " " tok;
  219.                 advance();
  220.                 }
  221.             #the type name proper
  222.             if (toktype == NAME)
  223.                 {
  224.                 var_type[var_number] = var_type[var_number] " " tok;
  225.                 advance();
  226.                 }
  227.             else
  228.                 require(0, "stat unexpected token while looking for param type");
  229.             }
  230.         else
  231.             {
  232.             var_type[++var_number] = tok;
  233.             advance();
  234.             }
  235.         # the parameter name (with adornments), optional
  236.         if (toktype == "*" || toktype == NAME)
  237.             star_name();
  238.         }
  239.     else
  240.         require(0, "unexpected token while looking for param type");
  241.     }
  242.  
  243. # star_name        :    {'*'}name{'['.*']'}
  244. function star_name()
  245.     {
  246.     if (tok == "*")
  247.         {
  248.         while (tok == "*")
  249.             {
  250.             var_name[var_number] = var_name[var_number] tok;
  251.             advance();
  252.             }
  253.         }
  254.     if (toktype == NAME)
  255.         {
  256.         var_name[var_number] = var_name[var_number] tok;
  257.         advance();
  258.         }
  259.     else
  260.         require(0, "unexpected token while looking for name");
  261.     while (tok == "[")
  262.         {
  263.         var_name[var_number] = var_name[var_number] tok;
  264.         advance();
  265.         while (tok != "]" && toktype != EOF)
  266.             {
  267.             if (tok == ",")
  268.                 var_name[var_number] = var_name[var_number] tok;
  269.             else
  270.                 var_name[var_number] = var_name[var_number] " " tok;
  271.             advance();
  272.             }
  273.         if (toktype != EOF)
  274.             {
  275.             var_name[var_number] = var_name[var_number] " " tok;
  276.             advance();
  277.             }
  278.         else
  279.             require(0, "unexpected end of clip while looking for ]");
  280.         }
  281.     }
  282.  
  283. # collect local declarations
  284. # local_decls        :    local_decl* to end of clip
  285. function local_decls()
  286.     {
  287.     while (toktype != EOF)
  288.         local_decl();
  289.     }
  290.  
  291. # local_decl        :    type_name local_name {',' {comment} local_name} ';' {comment}
  292. # note here var_number is incremented when local_name seen, not when type_name seen.
  293. function local_decl()
  294.     {
  295.     
  296.     if (toktype == NAME)
  297.         {
  298.         if (tok == "static" || tok == "const" || tok == "volatile"\
  299.         || tok == "extern" || tok == "register"\
  300.         || tok == "signed" || tok == "unsigned")
  301.             {
  302.             cur_var_type = tok;
  303.             advance();
  304.             while (tok == "static" || tok == "const" || tok == "volatile"\
  305.                 || tok == "extern" || tok == "register"\
  306.                 || tok == "signed" || tok == "unsigned")
  307.                 {
  308.                 cur_var_type = cur_var_type " " tok;
  309.                 advance();
  310.                 }
  311.             if (toktype == NAME)
  312.                 {
  313.                 cur_var_type = cur_var_type " " tok;
  314.                 advance();
  315.                 }
  316.             else
  317.                 require(0, "stat unexpected token while looking for local var type");
  318.             }
  319.         else
  320.             {
  321.             cur_var_type = tok;
  322.             advance();
  323.             }
  324.         if (toktype == "*" || toktype == NAME)
  325.             {
  326.             local_name();
  327.             # continue for all decls with same type, and pick up comments
  328.             while (tok != ";" && toktype != EOF)
  329.                 {
  330.                 if (tok == ",")
  331.                     advance();
  332.                 if (toktype == COMMENT)
  333.                     {
  334.                     # record the comment
  335.                     var_comment[var_number] = tok;
  336.                     advance();
  337.                     }
  338.                 if (toktype == "*" || toktype == NAME)
  339.                     {
  340.                     local_name();
  341.                     }
  342.                 # may lock up on eg a function declaration
  343.                 if (tok != ";" && tok != ",")
  344.                     require(0, "unexpected token while looking for local name or semicolon or EOF");
  345.                 }
  346.             if (tok == ";")
  347.                 advance();
  348.             if (toktype == COMMENT)
  349.                 {
  350.                 # record the comment
  351.                 var_comment[var_number] = tok;
  352.                 advance();
  353.                 }
  354.             }
  355.         else
  356.             require(0, "unexpected token while looking for local name");
  357.         }
  358.     else
  359.         require(0, "unexpected token while looking for local var type");
  360.     }
  361.  
  362. # local_name        :    star_name {'=' var_init}
  363. function local_name()
  364.     {
  365.     var_type[++var_number] = cur_var_type;
  366.     star_name();
  367.     if (tok == "=")
  368.         {
  369.         var_init[var_number] = " " tok;
  370.         #advance(); - get init now being done behind lexer's back via "match"
  371.         get_initter();
  372.         }
  373.     }
  374.  
  375. # var_init            :    stuff in '{}' or all up to ',' or ';' or EOF
  376. # -left unparsed, to avoid having to put whitespace back in
  377. function get_initter()
  378.     {
  379.     if (tok == "{")
  380.         {
  381.         if (match(clip, /^[^}]+}/))
  382.             {
  383.             var_init[var_number] = var_init[var_number] substr(clip, 1, RLENGTH);
  384.             clip = substr(clip, RLENGTH+1)
  385.             advance();
  386.             }
  387.         else
  388.             require(0, "unbalanced curly initializer");
  389.         }
  390.     else
  391.         {
  392.         if (match(clip, /^[^,;]+[,;]/))
  393.             {
  394.             var_init[var_number] = var_init[var_number] substr(clip, 1, RLENGTH-1);
  395.             clip = substr(clip, RLENGTH)
  396.             advance();
  397.             }
  398.         else
  399.             require(0, "unbalanced initializer");
  400.         }
  401. # Old version, putting whitespace back in proved too tedious....    
  402. #    previous_toktype = toktype;
  403. #    
  404. #    if (tok == "{")
  405. #        {
  406. #        while (tok != "}" && tok != ";" && toktype != EOF)
  407. #            {
  408. #            if (tok == "," || (tok == "(" && previous_toktype == NAME))
  409. #                var_init[var_number] = var_init[var_number] tok;
  410. #            else
  411. #                var_init[var_number] = var_init[var_number] " " tok;
  412. #            previous_toktype = toktype;
  413. #            advance();
  414. #            }
  415. #        if (tok == "}")
  416. #            {
  417. #            var_init[var_number] = var_init[var_number] " " tok;
  418. #            advance();
  419. #            }
  420. #        }
  421. #    while (tok != "," && tok != ";" && toktype != EOF)
  422. #        {
  423. #        if (tok == "(" && previous_toktype == NAME)
  424. #            var_init[var_number] = var_init[var_number] tok;
  425. #        else
  426. #            var_init[var_number] = var_init[var_number] " " tok;
  427. #        previous_toktype = toktype;
  428. #        advance();
  429. #        }
  430.     }
  431.  
  432.  
  433.  
  434. ## output functions **
  435. # Do the output to "out", which will become the new clip.
  436. function output_reformatted_beginning()
  437.     {
  438.     OutputDecl();
  439.     OutputLocals();
  440.     }
  441.  
  442. # Output func intro and nicely formatted params, and '{'.
  443. function OutputDecl(    longest_type, longest_name, first_inc)
  444.     {
  445.     # special handling for case of single param
  446.     if (last_param == 1)
  447.         {
  448.         if (var_name[1] != "")
  449.             var_type[1] = var_type[1] " ";
  450.         if (var_comment[1] != "")
  451.             var_comment[1] = "\t" var_comment[1];
  452.         else
  453.             var_comment[1] = "\t// "
  454.         out = opening "(" var_type[1] var_name[1] ")" var_comment[1] "\r\t{\r";
  455.         }
  456.     else if (last_param > 0)
  457.         {
  458.         out = opening "\r";
  459.         #calculate longest type and param name, and stick the commas in
  460.         longest_type = 0;
  461.         longest_name = 0;
  462.         for (i = 1; i <= last_param; ++i)
  463.             {
  464.             if (i == 1)
  465.                 var_type[i] = "(" var_type[i];
  466.             if (longest_type < length(var_type[i]))
  467.                 longest_type = length(var_type[i])
  468.             if (i < last_param)
  469.                 var_name[i] = var_name[i] ","
  470.             else
  471.                 var_name[i] = var_name[i] ")"
  472.             if (longest_name < length(var_name[i]))
  473.                 longest_name = length(var_name[i])
  474.             }
  475.         # adjust up to next tab boundary
  476.         if (longest_type % spaces_in_tab)
  477.             longest_type += spaces_in_tab - longest_type % spaces_in_tab;
  478.         if (longest_name % spaces_in_tab)
  479.             longest_name += spaces_in_tab - longest_name % spaces_in_tab;
  480.         # and add a tab
  481.         longest_type += spaces_in_tab;
  482.         longest_name += spaces_in_tab;
  483.         # now spit them out
  484.         for (i = 1; i <= last_param; ++i)
  485.             {
  486.             # adjust type names
  487.             diff = longest_type - length(var_type[i])
  488.             first_inc = spaces_in_tab - length(var_type[i]) % spaces_in_tab;
  489.             var_type[i] = "\t" var_type[i];
  490.             if (first_inc == spaces_in_tab)
  491.                 first_inc = 0;
  492.             if (first_inc)
  493.                 {
  494.                 var_type[i] = var_type[i] "\t";
  495.                 diff -= first_inc;
  496.                 }
  497.             while (diff > 0)
  498.                 {
  499.                 var_type[i] = var_type[i] "\t";
  500.                 diff -= spaces_in_tab;
  501.                 }
  502.             # adjust param names (if present)
  503.             if (var_name[i] != "")
  504.                 {
  505.                 diff = longest_name - length(var_name[i])
  506.                 first_inc = spaces_in_tab - length(var_name[i]) % spaces_in_tab;
  507.                 if (first_inc == spaces_in_tab)
  508.                     first_inc = 0;
  509.                 if (first_inc)
  510.                     {
  511.                     var_name[i] = var_name[i] "\t";
  512.                     diff -= first_inc;
  513.                     }
  514.                 while (diff > 0)
  515.                     {
  516.                     var_name[i] = var_name[i] "\t";
  517.                     diff -= spaces_in_tab;
  518.                     }
  519.                 }
  520.             # create dummy comment if none present
  521.             if (var_comment[i] == "")
  522.                 var_comment[i] = "// ";
  523.             # out go type, name, comment
  524.             out = out var_type[i] var_name[i] var_comment[i] "\r";
  525.             }
  526.         out = out "\t{\r";
  527.         }
  528.     }
  529.  
  530. # Output nicely formatted local definitions - note locals are optional
  531. function OutputLocals(    longest_type, longest_name, first_inc)
  532.     {
  533.     if (last_local <= last_param)
  534.         return;
  535.     #calculate longest type and param name
  536.     longest_type = 0;
  537.     longest_name = 0;
  538.     for (i = last_param+1; i <= last_local; ++i)
  539.         {
  540.         if (longest_type < length(var_type[i]))
  541.             longest_type = length(var_type[i])
  542.         if (longest_name < length(var_name[i]))
  543.             longest_name = length(var_name[i])
  544.         }
  545.     longest_name += 1; # 1 extra for ';'
  546.     # adjust up to next tab boundary
  547.     if (longest_type % spaces_in_tab)
  548.         longest_type += spaces_in_tab - longest_type % spaces_in_tab;
  549.     if (longest_name % spaces_in_tab)
  550.         longest_name += spaces_in_tab - longest_name % spaces_in_tab;
  551.     # and add a tab
  552.     longest_type += spaces_in_tab;
  553.     longest_name += spaces_in_tab;
  554.     # sneaky trick, attempt to squeeze short inits in with var_name
  555.     for (i = last_param+1; i <= last_local; ++i)
  556.         {
  557.         if (length(var_type[i]) + length(var_init[i]) + 1 <= longest_name)
  558.             {
  559.             var_name[i] = var_name[i] var_init[i];
  560.             delete var_init[i];
  561.             }
  562.         }
  563.     # now spit them out
  564.     for (i = last_param+1; i <= last_local; ++i)
  565.         {
  566.         # adjust type names
  567.         diff = longest_type - length(var_type[i])
  568.         first_inc = spaces_in_tab - length(var_type[i]) % spaces_in_tab;
  569.         var_type[i] = "\t" var_type[i];
  570.         if (first_inc == spaces_in_tab)
  571.             first_inc = 0;
  572.         if (first_inc)
  573.             {
  574.             var_type[i] = var_type[i] "\t";
  575.             diff -= first_inc;
  576.             }
  577.         while (diff > 0)
  578.             {
  579.             var_type[i] = var_type[i] "\t";
  580.             diff -= spaces_in_tab;
  581.             }
  582.         # adjust local names (always present), and append a semicolon, if no init
  583.         if (!(i in var_init) || var_init[i] == "")
  584.             {
  585.             var_name[i] = var_name[i] ";";
  586.             diff = longest_name - length(var_name[i])
  587.             first_inc = spaces_in_tab - length(var_name[i]) % spaces_in_tab;
  588.             if (first_inc == spaces_in_tab)
  589.                 first_inc = 0;
  590.             if (first_inc)
  591.                 {
  592.                 var_name[i] = var_name[i] "\t";
  593.                 diff -= first_inc;
  594.                 }
  595.             while (diff > 0)
  596.                 {
  597.                 var_name[i] = var_name[i] "\t";
  598.                 diff -= spaces_in_tab;
  599.                 }
  600.             }
  601.         else
  602.             var_name[i] = var_name[i] var_init[i] ";";
  603.         # create dummy comment if none present
  604.         if (var_comment[i] == "")
  605.             var_comment[i] = "// ";
  606.         # out go type, name, var_init, comment
  607.         #temp = var_type[i] var_name[i] var_init[i] var_comment[i];
  608.         # var_init has been thrown in with var_name just above
  609.         temp = var_type[i] var_name[i] var_comment[i];
  610.         out = out temp "\r";
  611.         }
  612.     }
  613.  
  614.  
  615. ## lexical functions ##
  616. function init()
  617.     {
  618.     NAME = 256
  619.     STRING = 257
  620.     CHAR_CONSTANT = 258
  621.     NUMBER = 259
  622.     KEY = 260
  623.     BITASSIGNOP = 261
  624.     ASSIGNOP = 262
  625.     LEX_OR = 263
  626.     LEX_AND = 264
  627.     RELOP = 265
  628.     SHIFT = 266
  629.     INCREMENT = 267
  630.     DECREMENT = 268
  631.     POINTER = 269
  632.     OTHER = 270
  633.     COMMENT = 271
  634.     EOF = 272
  635.     MEMBEROP = 273
  636.     ILLEGAL = 274
  637.     
  638.     }
  639.  
  640. # A simple, incomplete lexical analyzer for C - note it returns comments as tokens
  641. function advance()
  642.     {
  643.     if (clip == "" || error != 0)
  644.         {
  645.         toktype = EOF;
  646.         return;
  647.         }
  648.     if (toktype == EOF) return
  649.     if (tok == "." || tok == "->") #member coming, not a local
  650.         {
  651.         if (match(clip, /^[A-Za-z_](\w|_)*/))
  652.             {
  653.             tok = substr(clip, 1, RLENGTH)
  654.             clip = substr(clip, RLENGTH+1)
  655.             toktype = OTHER
  656.             return
  657.             }
  658.         else
  659.             require(0, "missing member name")
  660.         }
  661.     toktype = ILLEGAL
  662.     # grab comments, quotes, ticks, skip white
  663.     # if something was grabbed, including comment, return it now
  664.     if (skip_comments_etc())
  665.         return;
  666.     else if (clip == "" || error != 0)
  667.         {
  668.         toktype = EOF;
  669.         return;
  670.         }
  671.  
  672.     if (match(clip, /^[A-Za-z_](\w|_)*/) ||
  673.         match(clip, /^\.\.\./)) #name - note "..." treated as name.
  674.         {
  675.         tok = substr(clip, 1, RLENGTH)
  676.         clip = substr(clip, RLENGTH+1)
  677.         toktype = NAME
  678.         return
  679.         }
  680.     if (match(clip, /^([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)?[fFlL]?/) ||
  681.         match(clip, /^0[0-7]+(u|U)?(l|L)?/) ||
  682.         match(clip, /^0(x|X)[0-9a-fA-F]+(u|U)?(l|L)?/))    #float or int
  683.         toktype = NUMBER;
  684.     else if (match(clip, /^(<<=|>>=|&=|\^=|\|=)/))        #bit assign
  685.         toktype = BITASSIGNOP
  686.     else if (match(clip, /^(\+=|-=|\*=|\/=|%=)/))        #assign, inc
  687.         toktype = ASSIGNOP
  688.     else if (match(clip, /^\|\|/))
  689.         toktype = LEX_OR
  690.     else if (match(clip, /^&&/))
  691.         toktype = LEX_AND
  692.     else if (match(clip, /^(<=|==|!=|>=)/))                #relational
  693.         toktype = RELOP
  694.     else if (match(clip, /^(<<|>>)/))                    #shift
  695.         toktype = SHIFT
  696.     else if (match(clip, /^\+\+/))
  697.         toktype = INCREMENT
  698.     else if (match(clip, /^--/))
  699.         toktype = DECREMENT
  700.     else if (match(clip, /^->/) || match(clip, /^->\*/))
  701.         toktype = POINTER
  702.     else if (match(clip, /^\./) || match(clip, /^\.\*/))
  703.         toktype = MEMBEROP
  704.     else if (match(clip, /^./))                            #everything else
  705.         {
  706.         #TO DO trap illegal tokens, eg "@"
  707.         toktype = substr(clip,1,1)
  708.         }
  709.     else
  710.         require(0, "Unexpected empty clip");
  711.     tok = substr(clip, 1, RLENGTH)
  712.     clip = substr(clip, RLENGTH+1)
  713.     if (error != 0)
  714.         toktype = EOF;
  715.     }
  716.  
  717. # grab comments, quotes, ticks, skip white
  718. function skip_comments_etc()
  719.     {
  720.     sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  721.     # is it a one-line comment?
  722.     if (match(clip, /^\/\/[^\r]*\r/))
  723.         {
  724.         tok = substr(clip, 1, RLENGTH-1)
  725.         clip = substr(clip, RLENGTH+1)
  726.         sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  727.         toktype = COMMENT
  728.         # nice touch, put a space at start of comment if missing
  729.         if (!match(tok, /^\/\/ /))
  730.             tok = "// " substr(tok, 3);
  731.         return 1;
  732.         }
  733.     # or a multi-line comment?
  734.     else if(match(clip, /^\/\*/))
  735.         {
  736.         GetComment();
  737.         sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  738.         toktype = COMMENT
  739.         return 1;
  740.         }
  741.     # or a string?
  742.     else if(match(clip, /^"/))
  743.         {
  744.         GetString();
  745.         sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  746.         toktype = STRING
  747.         return 1;
  748.         }
  749.     # or a tick thingy?
  750.     else if(match(clip, /^'/))
  751.         {
  752.         GetCharConstant();
  753.         sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  754.         toktype = CHAR_CONSTANT
  755.         return 1;
  756.         }
  757.     # otherwise, return 0 meaning nothing significant captured
  758.     else
  759.         return 0;
  760.     }
  761.  
  762. # Extract /*...*/ style comment from clip
  763. function GetComment(        c, cp, i, len)
  764.     {
  765.     tok = "";
  766.     i = 4;
  767.     for (len = length(clip); i <= len; ++i)
  768.         {
  769.         c = substr(clip, i-1, 1);
  770.         cp = substr(clip, i, 1);
  771.         if (c == "*" && cp == "/")
  772.             {
  773.             tok = substr(clip, 1, i);
  774.             clip = substr(clip, i+1)
  775.             # Nice(?) touch, convert to inline if wanted
  776.             if (convert_comments)
  777.                 {
  778.                 sub(/^\/\*/, "//", tok);
  779.                 sub(/\*\/$/, "", tok);
  780.                 gsub(/\r[ \t]*/, "&// ", tok);
  781.                 # put a space at start of comment if missing
  782.                 if (!match(tok, /^\/\/ /))
  783.                     tok = "// " substr(tok, 3);
  784.                 }
  785.             return;
  786.             }
  787.         }
  788.     require(0, "unterminated comment")
  789.     }
  790.  
  791. # Get standard C string
  792. function GetString(        len, c, i, temp, esc)
  793.     {
  794.     
  795.     len = length(clip);
  796.     i = 2;
  797.     for (esc = 0; i <= len; ++i)
  798.         {
  799.         c = substr(clip, i, 1);
  800.         if (c == "\"")
  801.             {
  802.             if (esc == 0 || esc%2 == 0)
  803.                 {
  804.                 tok = substr(clip,1,i)
  805.                 clip = substr(clip, i+1)
  806.                 return;
  807.                 }
  808.             else
  809.                 esc = 0;
  810.             }
  811.         else if (c == "\\")
  812.             {
  813.             if (substr(clip, i+1, 1) == "\r") #end of line, string continued
  814.                 {
  815.                 esc = 0;
  816.                 }
  817.             else
  818.                 ++esc;
  819.             }
  820.         else if (c == "\r")
  821.             {
  822.             if (substr(clip, i-1, 1) == "\\") #string continued properly
  823.                 {
  824.                 ;
  825.                 }
  826.             else
  827.                 require(0, "unterminated string");
  828.             }
  829.         else
  830.             esc = 0;
  831.         }
  832.     require(0, "unterminated string");
  833.     }
  834.  
  835. # Get a thing in ticks''
  836. function GetCharConstant(        len, c, i, esc)
  837.     {
  838.     len = length(clip)
  839.     i = 2;
  840.     for (esc = 0; i <= len; ++i)
  841.         {
  842.         c = substr(clip, i, 1)
  843.         if (c == "'")
  844.             {
  845.             if (esc == 0 || esc%2 == 0)
  846.                 {
  847.                 tok = substr(clip,1,i)
  848.                 clip = substr(clip, i+1)
  849.                 return;
  850.                 }
  851.             else
  852.                 esc = 0
  853.             }
  854.         else if (c == "\\")
  855.             ++esc
  856.         else if (c == "\r")
  857.             require(0, "unterminated char constant")
  858.         else
  859.             esc = 0
  860.         }
  861.     require(0, "unterminated char constant")
  862.     }
  863.  
  864. # If assertion failed, stop work and print message to standard error
  865. # (Note contents of stderr are typically not flushed until you stop this program)
  866. function require(assertion, message)
  867.     {
  868.     if (assertion == 0)
  869.         {
  870.         error = 1;
  871.         print message >> "stderr";
  872.         toktype = EOF; # a bit of a hack, stop the lexer no matter what it takes
  873.         }
  874.     }
  875.  
  876.